// directory.cc 
//	Routines to manage a directory of file names.
//
//	The directory is a table of fixed length entries; each
//	entry represents a single file, and contains the file name,
//	and the location of the file header on disk.  The fixed size
//	of each directory entry means that we have the restriction
//	of a fixed maximum size for file names.
//
//	The constructor initializes an empty directory of a certain size;
//	we use ReadFrom/WriteBack to fetch the contents of the directory
//	from disk, and to write back any modifications back to disk.
//
//	Also, this implementation has the restriction that the size
//	of the directory cannot expand.  In other words, once all the
//	entries in the directory are used, no more files can be created.
//	Fixing this is one of the parts to the assignment.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "utility.h"
#include "filehdr.h"
#include "directory.h"

//----------------------------------------------------------------------
// Directory::Directory
// 	Initialize a directory; initially, the directory is completely
//	empty.  If the disk is being formatted, an empty directory
//	is all we need, but otherwise, we need to call FetchFrom in order
//	to initialize it from disk.
//
//	"size" is the number of entries in the directory
//----------------------------------------------------------------------

Directory::Directory(int size)
{
    table = new DirectoryEntry[size];
    tableSize = size;
    for (int i = 0; i < tableSize; i++)
	table[i].inUse = FALSE;
}

//----------------------------------------------------------------------
// Directory::~Directory
// 	De-allocate directory data structure.
//----------------------------------------------------------------------

Directory::~Directory()
{ 
    delete [] table;
} 

//----------------------------------------------------------------------
// Directory::FetchFrom
// 	Read the contents of the directory from disk.
//
//	"file" -- file containing the directory contents
//----------------------------------------------------------------------

void
Directory::FetchFrom(OpenFile *file)
{
    (void) file->ReadAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
}

//----------------------------------------------------------------------
// Directory::WriteBack
// 	Write any modifications to the directory back to disk
//
//	"file" -- file to contain the new directory contents
//----------------------------------------------------------------------

void
Directory::WriteBack(OpenFile *file)
{
    (void) file->WriteAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
}

//----------------------------------------------------------------------
// Directory::FindIndex
// 	Look up file name in directory, and return its location in the table of
//	directory entries.  Return -1 if the name isn't in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------

int
Directory::FindIndex(char *name,bool isDir)
{
    for (int i = 0; i < tableSize; i++)
        if (table[i].inUse&&(table[i].isDir == isDir) && !strncmp(table[i].name, name, FileNameMaxLen))
	    return i;
    return -1;		// name not in directory
}

//----------------------------------------------------------------------
// Directory::Find
// 	Look up file name in directory, and return the disk sector number
//	where the file's header is stored. Return -1 if the name isn't 
//	in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------

int
Directory::Find(char *name,bool isDir)
{
     DEBUG('f', "Directory::Find name %s\n",name);
    int i = FindIndex(name,isDir);

    if (i != -1)
	return table[i].sector;
    return -1;
}

//----------------------------------------------------------------------
// Directory::Add
// 	Add a file into the directory.  Return TRUE if successful;
//	return FALSE if the file name is already in the directory, or if
//	the directory is completely full, and has no more space for
//	additional file names.
//
//	"name" -- the name of the file being added
//	"newSector" -- the disk sector containing the added file's header
//----------------------------------------------------------------------

bool
Directory::Add(char *name, int newSector,bool isDir)
{ 
    if (FindIndex(name,isDir) != -1)
	return FALSE;

    for (int i = 0; i < tableSize; i++)
        if (!table[i].inUse) {
            
            table[i].isDir = isDir; // 添加是否是目录标记
            DEBUG('f', "Directory::Add isDir: %d name %s\n",table[i].isDir ,name);
            table[i].inUse = TRUE;
            strncpy(table[i].name, name, FileNameMaxLen); 
            table[i].sector = newSector;
        return TRUE;
	}
    return FALSE;	// no space.  Fix when we have extensible files.
}

//----------------------------------------------------------------------
// Directory::Remove
// 	Remove a file name from the directory.  Return TRUE if successful;
//	return FALSE if the file isn't in the directory. 
//
//	"name" -- the file name to be removed
//----------------------------------------------------------------------

bool
Directory::Remove(char *name)
{ 
    // 没适配 树装目录
    int i = FindIndex(name,FALSE);
    if(i == -1)
        i = FindIndex(name,TRUE);
    //TODO : 删除目录还没有做
    /*
    if (i == -1){
        i = FindIndex(name,TRUE);
        // 要删除的是目录
        //应该检查 目录下是否还有文件
    }
    */

    if (i == -1)
	return FALSE; 		// name not in directory
    table[i].inUse = FALSE;
    return TRUE;	
}

//----------------------------------------------------------------------
// Directory::List
// 	List all the file names in the directory. 
//----------------------------------------------------------------------

void
Directory::List()
{
    /*
   for (int i = 0; i < tableSize; i++)
	if (table[i].inUse)
	    printf("%s\n", table[i].name);
    */
   for(int i = 0;i < tableSize;i++){
       if(table[i].inUse){
           if(table[i].isDir){
               //是目录
                printf("../%s----|\n",table[i].name);
                innerList(table[i].sector,1);
           }else{
               //是文件
               printf("../%s\n",table[i].name);
           }
       }
   }

}



void Directory::innerList(int sector,int level){
    Directory *directory  = new Directory(tableSize);
    OpenFile* dirFile = new OpenFile(sector);
    directory->FetchFrom(dirFile);
    for(int i = 0;i < tableSize;i++){
        if(directory->table[i].inUse){
            // 打印空格
            for(int k = 0; k < level;k++)
                printf("          ");
            if(directory->table[i].inUse){
                if(directory->table[i].isDir){
                    //是目录
                    printf("/%s----|\n",directory->table[i].name);
                    innerList(directory->table[i].sector,level+1);
                }else{
                    //是文件
                    printf("/%s\n",directory->table[i].name);
                }
            }
        }
    }
}
//----------------------------------------------------------------------
// Directory::Print
// 	List all the file names in the directory, their FileHeader locations,
//	and the contents of each file.  For debugging.
//----------------------------------------------------------------------

void
Directory::Print()
{ 
    FileHeader *hdr = new FileHeader;

    printf("Directory contents:\n");
    /*
    for (int i = 0; i < tableSize; i++)
	    if (table[i].inUse) {
	    printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
	    hdr->FetchFrom(table[i].sector);
	    hdr->Print();
	}*/
    for (int i = 0; i < tableSize; i++)
	    if (table[i].inUse) {
            if(table[i].isDir){
                innerPrint(table[i].sector);
            }else{
                // 是文件
	            printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
	            hdr->FetchFrom(table[i].sector);
	            hdr->Print();
            }
	}
    printf("\n");
    delete hdr;
}


void
Directory::innerPrint(int sector){
    Directory *directory  = new Directory(tableSize);
    OpenFile* dirFile = new OpenFile(sector);
    FileHeader* hdr = new FileHeader;
    directory->FetchFrom(dirFile);
    for(int i = 0;i < tableSize;i++){
        if(directory->table[i].inUse){
            if(directory->table[i].inUse){
                if(directory->table[i].isDir){
                    innerPrint(directory->table[i].sector);
                }else{
                    //是文件
                    printf("Name: %s, Sector: %d\n",directory->table[i].name,directory->table[i].sector);
                    hdr->FetchFrom(directory->table[i].sector);
                    hdr->Print();
                }
            }
        }
    }
}

//----------------------------------------------------------------------
// Directory::Count
// 	当前目录中有效文件和子目录数量
//----------------------------------------------------------------------
int Directory::Count(){
    int cnt = 0;
    for (int i = 0; i < tableSize; i++)
	    if (table[i].inUse) 
            cnt++;
    return cnt;
}